package tests;

import java.io.*; 

import global.*;
import bufmgr.*;
import diskmgr.*;
import heap.*;
import iterator.*;
import index.*;

import java.util.Random;


class MYJoinTest extends TestDriver 
  implements GlobalConst {

  private static int   LARGE = 1000; 
  private static int   SORTPGNUM = 12; 
  private static Random rand = new Random((long)1000);


  public MYJoinTest() {
    super("jointest");
  }

  public boolean runTests ()  {
    
    SystemDefs sysdef = new SystemDefs( dbpath, 300, NUMBUF, "Clock" );

    // Kill anything that might be hanging around
    String newdbpath;
    String newlogpath;
    String remove_logcmd;
    String remove_dbcmd;
    String remove_cmd = "/bin/rm -rf ";

    newdbpath = dbpath;
    newlogpath = logpath;

    remove_logcmd = remove_cmd + logpath;
    remove_dbcmd = remove_cmd + dbpath;

    // Commands here is very machine dependent.  We assume
    // user are on UNIX system here
    try {
      Runtime.getRuntime().exec(remove_logcmd);
      Runtime.getRuntime().exec(remove_dbcmd);
    } 
    catch (IOException e) {
      System.err.println (""+e);
    }
    
    remove_logcmd = remove_cmd + newlogpath;
    remove_dbcmd = remove_cmd + newdbpath;

    //This step seems redundant for me.  But it's in the original
    //C++ code.  So I am keeping it as of now, just in case I
    //I missed something
    try {
      Runtime.getRuntime().exec(remove_logcmd);
      Runtime.getRuntime().exec(remove_dbcmd);
    } 
    catch (IOException e) {
      System.err.println (""+e);
    }

    //Run the tests. Return type different from C++
    System.out.println("Join Test 1: Join on vector fields.");
    boolean _pass = joinTest();

    //Clean up again
    try {
      Runtime.getRuntime().exec(remove_logcmd);
      Runtime.getRuntime().exec(remove_dbcmd);
    } 
    catch (IOException e) {
      System.err.println (""+e);
    }
    
    System.out.println ("\n" + "...Vector Join tests ");
    System.out.println (_pass==OK ? "completely successfully" : "failed");
    System.out.println (".\n\n");
    
    return _pass;
  }
  
  private boolean joinTest()
  {
	  boolean status = OK;
	  
	   AttrType[] attrType = new AttrType[2];
	   attrType[0] = new AttrType(AttrType.attrInteger);
	   attrType[1] = new AttrType(AttrType.attrVector100D);
	   TupleOrder[] order = new TupleOrder[2];
	   order[0] = new TupleOrder(TupleOrder.Ascending);
	   order[1] = new TupleOrder(TupleOrder.Descending);
	   
	    // Create unsorted data file 
	    RID             rid;
	    Heapfile        f = null, f2 = null;
	    try 
	    {
	      f = new Heapfile("testvj1.in");
	      f2 = new Heapfile("testvj2.in");
	    }
	    catch (Exception e) {
	      status = FAIL;
	      e.printStackTrace();
	    }
	    // prep tuple
	    Tuple t = new Tuple();

	    try {
	      t.setHdr((short) 2, attrType, null);
	    }
	    catch (Exception e) {
	      System.err.println("*** error in Tuple.setHdr() ***");
	      status = FAIL;
	      e.printStackTrace();
	    }

	    int size = t.size();

	    t = new Tuple(size);
	    try {
	      t.setHdr((short) 2, attrType, null);
	    }
	    catch (Exception e) {
	      status = FAIL;
	      e.printStackTrace();
	    }
	    
	    // insert some random records
	    status = insertRecords(f, t);
	    status = insertRecords(f2,t);
	   
	    // create an iterator by open a file scan
	    FldSpec[] projlist = new FldSpec[2];
	    RelSpec rel = new RelSpec(RelSpec.outer); 
	    projlist[0] = new FldSpec(rel, 1);
	    projlist[1] = new FldSpec(rel, 2);
	    
	    FileScan fscan = null, fscan2 = null;
	    
	    try {
	      fscan = new FileScan("testvj1.in", attrType, null, (short) 2, 2, projlist, null);
	      fscan2 = new FileScan("testvj2.in", attrType, null, (short) 2, 2, projlist, null);
	    }
	    catch (Exception e) {
	      status = FAIL;
	      e.printStackTrace();
	    }
	    
	    // we need a target vector for the sort
	    Vector100Dtype target = controlVector();
	    target.setIndex(99, (short) 1);
	    
	    Sort sort = null;
	    try
	    {
	    	int k = 10;
	    	sort = new Sort(attrType, (short) 2, null, fscan2, 2, order[0], 200, 12, target, k);
	    }
	    catch (Exception e) 
	    {
	        status = FAIL;
	        e.printStackTrace();
	    }
	    
	    CondExpr[] expr = new CondExpr[2];
	    expr[0] = new CondExpr();
	    expr[0].next  = null;
	    expr[0].op    = new AttrOperator(AttrOperator.aopLT);
	    expr[0].type1 = new AttrType(AttrType.attrSymbol);
	    expr[0].type2 = new AttrType(AttrType.attrSymbol);
	    expr[0].operand1.symbol = new FldSpec (new RelSpec(RelSpec.outer),2);
	    expr[0].operand2.symbol = new FldSpec (new RelSpec(RelSpec.innerRel),2);
	    expr[0].distance = 50;
	    expr[1] = null;

	    FldSpec[] proj2 = new FldSpec[4];
	    RelSpec rel1 = new RelSpec(RelSpec.outer); 
	    proj2[0] = new FldSpec(rel1, 1);
	    proj2[1] = new FldSpec(rel1, 2);
	    RelSpec rel2 = new RelSpec(RelSpec.innerRel);
	    proj2[2] = new FldSpec(rel2, 1);
	    proj2[3] = new FldSpec(rel2, 2);

	    
	    NestedLoopsJoins nlj = null;
		try {
			nlj = new NestedLoopsJoins(attrType, null, attrType, null, 10, 
					fscan, sort, expr, null, proj2);
		} catch (Exception e) {
			status = FAIL;
			e.printStackTrace();
		} 
		
	    status = iterateJoin(nlj);
	    
	    // clean up
	    try 
	    {
	      sort.close();
	      nlj.close();
	    }
	    catch (Exception e) {
	      status = FAIL;
	      e.printStackTrace();
	    }
	    return status;
  }
  
  private boolean insertRecords(Heapfile f, Tuple t)
  {
	  boolean status = OK;
	    // insert some random records
	    int inum;
	    Vector100Dtype vector;
	    RID rid;
	    
	    for (int i=0; i<1000; i++) 
	    {
	        // setting fields
	    	if (rand.nextInt(50) == 10)
	    	{
	    		vector = controlVector();
	    	}
	    	else
	    	{
	    		vector = getRandomVector();
	    	}
	        inum = rand.nextInt(1000);
	        try 
	        {
	        	t.setIntFld(1, inum);
	        	t.set100DVectFld(2, vector);
	        }
	        catch (Exception e) {
	        	status = FAIL;
	        	e.printStackTrace();
	        }

	        try 
	        {
	        	rid = f.insertRecord(t.returnTupleByteArray());
	        }
	        catch (Exception e) {
	        	status = FAIL;
	        	e.printStackTrace();
	        }
	      }
	    return status;
  }
  
  
  private boolean iterateJoin(Iterator nlj)
  {
	    Vector100Dtype v1, v2;
	    double d;
	    boolean status = OK;
	    int rec1, rec2;
	    Tuple t = null;

	    try
	    {
	    	t = nlj.get_next();
	    }
	    catch (Exception e) 
	    {
	        status = FAIL;
	        e.printStackTrace();
	    }
	    
	    while (t!=null)
	    {
	    	try
	    	{
	    		v1 = t.get100DVectFld(2);
	    		v2 = t.get100DVectFld(4);
	    		rec1 = t.getIntFld(1);
	    		rec2 = t.getIntFld(3);
	    		
	    		d = Vector100Dtype.Distance(v1, v2);
	    		System.out.format("%d joined to %d, distance: %f\n", rec1, rec2, d);
	    		
	    	}
		    catch (Exception e) 
		    {
		        status = FAIL;
		        e.printStackTrace();
		    }
		    try
		    {
		    	t = nlj.get_next();
		    }
		    catch (Exception e) 
		    {
		        status = FAIL;
		        e.printStackTrace();
		    }
	    }

	    if (status)
	    {
	    	System.out.println("Join Test: Join of two tables on a vector successful.");
	    }
	    return status;
  }

  private Vector100Dtype controlVector()
  {
	  Vector100Dtype v = new Vector100Dtype();
	  for (int i=0;i<99;i++)
	  {
		  v.setIndex(i, (short) 1);
		  v.setIndex(99, (short) rand.nextInt(1000));
	  }
	  return v;
  }
 private Vector100Dtype getRandomVector()
  {
	   Vector100Dtype vector = new Vector100Dtype();
	   short val;
	   
	   for (int i=0;i<100;i++)
	   {
		   val = (short)rand.nextInt(10001);
		   if (rand.nextInt(100) < 50)
		   {
			   val = (short) -val;
		   }
		   vector.setIndex(i, val);
	   }
	   return vector;
  }
}
